using System;
using System.Collections.Generic;
using System.Text;

namespace AndersLiu.Samples.EnumSimilarClass
{
	/// <summary>
	/// Indicates the surpported document types.
	/// You cannot inherits from this class.
	/// </summary>
	/// <remarks>
	/// This class looks very like an enum.
	/// </remarks>
	public sealed class DocumentType
	{
		#region foundation supports
		static Dictionary<string, DocumentType> _allTypes;

		// Indicates the documents' type name.
		const string WordTypeName = "Word";
		const string HtmlTypeName = "Html";
		const string TextTypeName = "Text";

		// Indicates which application can be used to open the document.
		const string WordProcessor = "winword.exe";
		const string HtmlProcessor = "iexplorer.exe";
		const string TextProcessor = "notepad.exe";

		// Indicates the file extension of each document type.
		const string WordExtension = ".doc";
		const string HtmlExtension = ".html";
		const string TextExtension = ".txt";

		/// <summary>
		/// Static constructor. Initializes all supported document types.
		/// </summary>
		static DocumentType()
		{
			_allTypes = new Dictionary<string, DocumentType>();

			_allTypes.Add(WordTypeName, new DocumentType(WordTypeName, WordProcessor, WordExtension));
			_allTypes.Add(HtmlTypeName, new DocumentType(HtmlTypeName, HtmlProcessor, HtmlExtension));
			_allTypes.Add(TextTypeName, new DocumentType(TextTypeName, TextProcessor, TextExtension));
		}
		#endregion

		#region enum similar members
		/// <summary>
		/// Word document.
		/// </summary>
		public static DocumentType Word
		{
			get
			{
				return _allTypes[WordTypeName];
			}
		}

		/// <summary>
		/// Html document.
		/// </summary>
		public static DocumentType Html
		{
			get
			{
				return _allTypes[HtmlTypeName];
			}
		}

		/// <summary>
		/// Text document.
		/// </summary>
		public static DocumentType Text
		{
			get
			{
				return _allTypes[TextTypeName];
			}
		}
		#endregion

		#region type convert oeprators
		/// <summary>
		/// Implicit convert <see cref="DocumentType"/> object to <see cref="string"/>.
		/// </summary>
		/// <param name="type">A given document type.</param>
		/// <returns>The type name.</returns>
		public static implicit operator string(DocumentType type)
		{
			return type.Name;
		}

		/// <summary>
		/// Explicit convert <see cref="string"/> object to <see cref="DocumentType"/>.
		/// </summary>
		/// <param name="typeName">Given document type name.</param>
		/// <returns>The corresponsive document type.</returns>
		public static explicit operator DocumentType(string typeName)
		{
			if(_allTypes.ContainsKey(typeName))
				return _allTypes[typeName];
			else
				throw new InvalidOperationException(string.Format("'{0}' is not a valid document type.", typeName));
		}
		#endregion

		#region other functions
		/// <summary>
		/// Indicates a given type name is valid or not.
		/// </summary>
		/// <param name="typeName">Given type name.</param>
		/// <returns>Whether the <see cref="typeName"/> is vliad.</returns>
		public static bool IsValidDocumetTypeName(string typeName)
		{
			return _allTypes.ContainsKey(typeName);
		}

		/// <summary>
		/// Override. Generate the string representation of the current object.
		/// </summary>
		/// <returns>The string representation.</returns>
		public override string ToString()
		{
			return string.Format(
				"{0} (Processor=[{1}], Extension=[{2}])",
				_name,
				_processor,
				_extension);
		}
		#endregion

		#region instanse members
		string _name;
		string _processor;
		string _extension;

		/// <summary>
		/// Initialize a <see cref="DocumentType"/> instance.
		/// This constructor is private, so that user cannot construct it from external.
		/// </summary>
		/// <param name="name">Name of the document type.</param>
		/// <param name="processor">Processor of the document.</param>
		/// <param name="extension">Extension of the document.</param>
		private DocumentType(string name, string processor, string extension)
		{
			_name = name;
			_processor = processor;
			_extension = extension;
		}

		/// <summary>
		/// Name of the document type.
		/// </summary>
		public string Name
		{
			get
			{
				return _name;
			}
		}

		/// <summary>
		/// Processor of the document.
		/// </summary>
		public string Processor
		{
			get
			{
				return _processor;
			}
		}

		/// <summary>
		/// Extension of the document.
		/// </summary>
		public string Extension
		{
			get
			{
				return _extension;
			}
		}
		#endregion
	}
}
